package unv.esiag.uchiha.topics;

import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.Vector;

import unv.esiag.uchiha.core.Domaine;
import unv.esiag.uchiha.operations.DataObject;
import unv.esiag.uchiha.operations.Operation;
import unv.esiag.uchiha.readers.DataReader;
import unv.esiag.uchiha.semaphore.Semaphore;
import unv.esiag.uchiha.subscribers.Subscriber;
import unv.esiag.uchiha.writer.DataWriter;

public class Topic {
	private Vector<TopicListener> listeners;
	private Semaphore listenersS;
	private String name;
	private Vector<Topic> topics;
	private DataReader dr;
	private DataWriter dw;
	private DataObject data;
	public Topic(String name) {
		this.listenersS = new Semaphore();
		this.listeners = new Vector<TopicListener>();
		this.name = name;
		this.topics = new Vector<Topic>();
		this.dr = new DataReader(this);
		this.dw = new DataWriter();
		this.data = new DataObject(name, null);
	}
	public void addTopicListener(TopicListener listener){
		this.listenersS.stop();
		this.listeners.add(listener);
		this.listenersS.signal();
	}
	public boolean addRemoteSubscriber(Subscriber subscriber){
		boolean res = getDataWriter().getPublisher().addRemoteSubscriber(subscriber);
		if(res){
			notifyListenersTopic();
		}
		return res;
	}
	public Topic getTopicWithFullName(String name){
		int index = name.indexOf('.');
		if(index != -1){
			String nametmp = name.substring(0, index);
			if(this.name.equals(nametmp)){
				name = name.substring(index + 1);
				for(int i = 0;i<topics.size();i++){
					Topic topic = getTopicWithFullName(name);
					if(topic != null){
						return topic;
					}
				}
			}
		}
		if(this.name.equals(name)){
			return this;
		}
		return null;
	}
	public void setData(Serializable serializable){
		this.data.setSerializable(serializable);
		notifyListenersCache();
	}
	public Serializable getData(){
		return this.data.getSerializable();
	}
	
	public STopic createSTopic(){
		STopic stopic = new STopic(getSimpleName(), getData());
		stopic.setFullName(name);
		stopic.setIor(getIor());
		for(int i = 0;i<this.topics.size();i++){
			stopic.addTopic(this.topics.get(i).createSTopic());
		}
		return stopic;
	}
	
	synchronized public void createFromSTopic(STopic stopic)throws RemoteException{
		for(int i = 0;i<stopic.size();i++){
			Topic topic = createTopic(stopic.get(i).getName());
			topic.createFromSTopic(stopic.get(i));
			topic.setData(stopic.get(i).getSerializable());
			Subscriber subscriber = Domaine.getInstance().ior_to_subscriber(stopic.get(i).getIor());
			if(topic.addRemoteSubscriber(subscriber)){
				DataObject data = new DataObject(topic.getName(), topic.getIor());
				Operation operation = new Operation(Operation.TARGET_TOPIC, Operation.TYPE_SETSUBSCRIBER, data);
				try{
					subscriber.publish(operation);
				}catch(Exception e){
					throw new RemoteException("Connexion Closed");
				}
			}
		}
	}
	
	public void notife(String notife){
		this.listenersS.stop();
		for(int i = 0;i<this.listeners.size();i++){
			this.listeners.get(i).notify(notife);
		}
		this.listenersS.signal();
	}
	
	public void notifyListenersTopic(){
		this.listenersS.stop();
		for(int i = 0;i<this.listeners.size();i++){
			this.listeners.get(i).topicModified(this);
		}
		this.listenersS.signal();
	}
	public void notifyListenersCache(){
		this.listenersS.stop();
		for(int i = 0;i<this.listeners.size();i++){
			this.listeners.get(i).cacheModified(this);
		}
		this.listenersS.signal();
	}
	public String getIor() {
		return dr.getReactor().getSubscriber().getIor();
	}
	public DataReader getDataReader(){
		return this.dr;
	}
	public DataWriter getDataWriter(){
		return this.dw;
	}
	public Topic createTopic(String name){
		Topic topic = this.getTopic(name);
		if(topic == null){
			if(!this.name.equals("Rooot")){
				name = this.name + "." + name;
			}
			topic = new Topic(name);
			topics.add(topic);
			this.listenersS.stop();
			for(int i = 0;i<this.listeners.size();i++){
				topic.addTopicListener(this.listeners.get(i));
			}
			this.listenersS.signal();
			notifyListenersTopic();
		}
		return topic;
	}
	public Topic getTopic(String name){
		for(int i = 0;i<this.topics.size();i++){
			if(this.topics.get(i).getSimpleName().equals(name)){
				return this.topics.get(i);
			}
		}
		return null;
	}
	public String getSimpleName(){
		int index = this.name.lastIndexOf('.');
		String simpleName = this.name;
		if(index != -1){
			simpleName = this.name.substring(index + 1);
		}
		return simpleName;
	}
	public String getName() {
		return name;
	}
	
	public int getLevel(){
		int k = 0;
		for(int i = 0;i<name.length();i++){
			if(name.charAt(i) == '.'){
				k++;
			}
		}
		return k;
	}

	public String toString() {
		StringBuilder sb = new StringBuilder();
		if(!this.name.equals("Rooot")){
			for(int i = 0;i<getLevel();i++){
				sb.append('-');
			}
			String className = "null";
			if(getData() != null){
				className = getData().getClass().getSimpleName();
			}
			sb.append(getSimpleName() + "[" + className + "]" + "\n");
		}
		for(int i = 0;i<topics.size();i++){
			sb.append(topics.get(i).toString());
		}
		return sb.toString();
	}
}
